/*
 * -------------------------------------- 
 * FileName: 1_2.employee_manager.aio.c 
 * Content:
 *      单文件1_2任务
 * Auther:  杨则瑞 
 * Encode:  utf-8 | CRLF                  
 * -------------------------------------- 
 */

#ifdef DEBUG_MODE

    #ifdef _WIN32

        #include<windows.h>
        #define _debug(_msg,_type)\
            do{\
                MessageBeep(0);\
                MessageBox(NULL,_msg,_type,0); \
            }while(0)
        
    #endif

    #ifdef __linux__

        #define _debug(_msg,_type)\
            do{
                fprintf(stderr,"[%s]:%s",_msg,_type)\
            }while(0)
    
    #endif

#else

    #define _debug(_msg,_type)\
    do {} while (0)

#endif


#include<stdio.h>
#include<string.h>
#include<stdint.h>
#include<malloc.h>
#include<memory.h>




/**
 * =====================
 *  双向链表声明
 * =====================
 */

//命名空间宏

#define ns(lable) duList_##lable

//链表错误
typedef enum ns(error_t)
{
    ns(OK),
    ns(INSUFFICIENT_MEMORORY),
    ns(INDEX_OUT_OF_BOUND),

} ns(error_t);

// 链表节点
typedef struct ns(node_t)  
{
    struct ns(node_t) *next;
    struct ns(node_t) *prev;
    int data[0];

} ns(node_t);

// 链表
typedef struct ns(t)
{
    struct ns(node_t) *_header;
    int _elem_size;
    ns(error_t)* _error_handler;

} ns(t);



int ns(init)   (ns(t) * self, size_t elem_size, ns(error_t)* error_handler);
int ns(uninit) (ns(t) * self);

int ns(insert) (ns(t) * self, size_t pos,const void * element);
int ns(insert_reverse) (ns(t) * self, size_t pos, const void * element);
int ns(erase)  (ns(t) * self, size_t pos);
int ns(erase_reverse)  (ns(t) * self, size_t pos);
int ns(clear)  (ns(t) * self);

int    ns(is_empty) (const ns(t) * self);
size_t ns(length)   (const ns(t) * self);
int    ns(get)      (const ns(t) * self, size_t pos, void * val_dest);
int    ns(get_reverse) (const ns(t) * self, size_t pos, void * val_dest);

#undef ns





/**
 * =====================
 *  主程序
 * =====================
 */

typedef struct employee_t
{

    // 唯一员工id
    char id[20]; 
    // 员工姓名
    char name[256];
    // 员工职务
    char position[512];
 
} employee_t;

// 已辞职员工表
duList_t archivedEmployees;

// 现有员工表
duList_t employees;

//欢迎
const char motto[] = 
"====================================================================== \n"\
"      Task 1 :      Employee Manage System  (linked list):         \n"\
"---------------------------------------------------------------------- \n"\
"    welcome! You can use \"help\" command to get helper docs           \n"\
"                                                                       \n"\
"                                                                       \n"\
"====================================================================== \n";

//帮助文档
const char helping[] = 
"======================================================================\n"
"add : \"add <id> <name> [discription]\"\n"
"   add a employee to currentEmployee list.\n"
"del : \"del <id> [-f(permanently)]\"\n"
"   del a employee from list, move it to Disinged employ list\n"
"       if \'-f\' is set, delete the entry permanently.\n"
"list : \"list [-a(show disigned)]\".\n"
"   list employee list.\n"
"show : \"show <id>\"\n"
"   print the name and discription of the entry with same id\n"
"quit : \"quit\".\n"
"   quit the programme\n"
"help : \"help\".\n"
"   print this page.\n"
"======================================================================\n";


static void cmd_list(const char *args);
static void cmd_quit(const char *);
static void cmd_add(const char * args);
static void cmd_query(const char * args);
static void cmd_help(const char * args);
static void cmd_delete(const char * args);



void init()
{
    printf("initing...\n");

    // 初始化数据结构
    duList_error_t app_erros = duList_OK;
    duList_init(&archivedEmployees, sizeof(employee_t), &app_erros);
    duList_init(&employees, sizeof(employee_t), &app_erros);

    if(app_erros != duList_OK)
    {
        printf("Encounter errors in initing database.\n");
        return;
    }

    printf(motto);
}


int running = 0;

int main(int argc, char * argv[])
{
    char cmd_buffer[1024];


    init();

    struct cmd_t {
        char command_name[20];
        void (*command)  (const char *);
    };

    struct cmd_t cmds[] = {
       {"list", cmd_list},
       {"add",  cmd_add},
       {"quit", cmd_quit},
       {"del", cmd_delete},
       {"show", cmd_query},
       {"help", cmd_help}
    };

    running = 1;
    while(running)
    {
        printf("\r>");
        fflush(stdout);

        //获取用户一行的输入
        fgets(cmd_buffer,1000,stdin);

        char ins[20];
        int offset;

        // 从用户输入的内容里找命令
        int all_len = strlen(cmd_buffer);
        sscanf(cmd_buffer,"%19s%n",ins,&offset);

        // 遍历所有命令，找到对应的，就执行
        unsigned char matched = 0;
        for(int i = 0; i < sizeof(cmds) / sizeof(struct cmd_t); ++ i)
        {
            if(0 == strcmp(cmds[i].command_name,ins) && all_len > offset)
            {
                cmds[i].command(cmd_buffer+offset);
                matched = 1;
            }
        }
        if(matched == 0)
        {
            printf("unrecongized command: %19s\n",ins);
        }
    }
    return 0;
}




static void cmd_list(const char *args)
{
    // 是否显示全部的 flag.
    unsigned char show_all_flag = 0;

    // 捕获参数，判断是否含有 'a'.
    char param[20];
    sscanf(args, "%19s", param);
    int n = strlen(param);
    for(int i = 0; i < n; ++ i)
    {
        if(param[i] == 'a')
        {
            show_all_flag = 1;
        }
    }

    // 列举当前员工列表
    size_t length = duList_length(&employees);
    printf("List of current employees, %u in total.\n", length);
    for(int i = 0; i < length; ++ i)
    {
        employee_t tmp;
        (duList_get(&employees,i,&tmp));
        printf("id:%s name:%s\n", tmp.id, tmp.name);
    }

    // 列举已辞职员工列表
    length = duList_length(&archivedEmployees);
    if(show_all_flag)
    {
        printf("List of disigned employees, %u in total.\n", length);
        for(int i = 0; i < length; ++ i)
        {
            employee_t tmp;
            (duList_get(&archivedEmployees,i,&tmp));
            printf("(disigned)id:%s name:%s\n", tmp.id, tmp.name);
        }
    }
}

static void cmd_quit(const char * _args)
{
    duList_uninit(&archivedEmployees);
    duList_uninit(&employees);

    running = 0;
}

static void cmd_add(const char * args)
{
    employee_t tmp;
    *tmp.position = '\0';
    
    int nums = sscanf(args,"%19s%255s%511s", tmp.id, tmp.name, tmp.position);
    
    //参数不合法
    if(nums < 2)
    {
        printf("add : \"add <id> <name> [discription]\".\n");
        return;
    }

    // 遍历员工
    size_t length = duList_length(&employees);
    for(int i = 0; i < length; ++ i)
    {
        employee_t itr;
        duList_get(&employees, i, &itr);
        if(0==strcmp(tmp.id,(itr.id)))
        {
            printf("duplicate id (same as %s's). not added.\n", itr.name);
            return;
        }
    }

    // 遍历已经辞职的员工。注：员工虽然辞职了但是id不能冲突，除非强制删除。
    length = duList_length(&archivedEmployees);
    for(int i = 0; i < length; ++ i)
    {
        employee_t itr;
        duList_get(&archivedEmployees, i, &itr);
        if(0==strcmp(tmp.id,(itr.id)))

        {
            printf("duplicate id (same as %s(resigned)'s). not added.\n", itr.name);
            return;
        }
    }


    duList_insert(&employees, length, &tmp);
}

static void cmd_delete(const char * args)
{
    unsigned char force_delete_flag = 0;

    char id[21];
    char param[21];

    int num = sscanf(args, "%20s%20s", id, param);
    
    //参数不合法
    if(num < 1)
    {
        printf("del : \"del <id> [-f(fore delete)]\"\n");
        return;
    }

    if(num == 2)
    {
        int len = strlen(param);
        for(int i = 0; i < len; ++ i)
        {
            if(param[i] == 'f')
            {
                force_delete_flag = 1;
            }
        }
    }

    //遍历所有员工
    size_t length = duList_length(&employees);
    for(int i = 0; i < length; ++ i)
    {
        employee_t tmp;

        // 匹配员工 id
        duList_get(&employees, i, &tmp);
        if(0==strcmp(id,(tmp.id)))
        {
            // 如果没有设置 -f 就把这个插入到辞职员工表里面。
            if(force_delete_flag == 0)
            {
                duList_insert(&archivedEmployees,0,&tmp);    
            }

            duList_erase(&employees,i);
            return;
        }
    }

    // 如果 -f 加上了，就会去从辞职员工列表里查找、删除。
    if(force_delete_flag)
    {
        length = duList_length(&archivedEmployees);
        for(int i = 0; i < length; ++ i)
        {
            employee_t tmp;
            duList_get(&archivedEmployees, i, &tmp);
            if(0==strcmp(id,(tmp.id)))
            {
                duList_erase(&archivedEmployees,i);
                return;
            }
        }
    }
    

    printf("not found.\n");

}

static void cmd_query(const char * args)
{
    char id[20];
    char param[20];

    int num = sscanf(args, "%20s%20s", id, param);

    // 参数非法
    if(num < 1)
    {
        printf("show : \"show <id>\"\n");
        return;
    }
    

    //遍历所有员工
    size_t length = duList_length(&employees);
    for(int i = 0; i < length; ++ i)
    {
        employee_t tmp;
        duList_get(&employees, i, &tmp);
        if(0==strcmp(id,(tmp .id)))
        {
            printf("name: %s\ndiscription: %s\n", tmp.name, tmp.position);
            return;
        }
    }

    length =  duList_length(&archivedEmployees);
    for(int i = 0; i < length; ++ i)
    {
        employee_t tmp;
        duList_get(&archivedEmployees, i, &tmp);
        if(0==strcmp(id,(tmp .id)))
        {
            printf("name: %s\ndiscription: %s\n", tmp.name, tmp.position);
            return;
        }
    }

    printf("Not found matching\n");
}

static void cmd_help(const char * _args)
{
    printf(helping);
    return;
}



/**
 * =====================
 *  双向链表实现
 * =====================
 */




#define ns(lable) duList_##lable

#define try_error(errors) \
do { \
    if(self->_error_handler != NULL)\
    { \
        *self->_error_handler = errors; \
    } \
} while (0)

// Memory
int ns(init) (ns(t) * self, size_t elem_size, ns(error_t)* error_handler)
{
    self->_elem_size = elem_size;
    self->_header = (ns(node_t) *)malloc(sizeof(ns(node_t)));
    if(error_handler != NULL)
    {
        self->_error_handler = error_handler;
    }

    if(self->_header == NULL)
    {
        _debug("Allocation Fault","WARN");

        try_error(duList_INSUFFICIENT_MEMORORY);

        return -1;
    }

    self->_header->next = self->_header;
    self->_header->prev = self->_header;
    return 0;
}


int ns(uninit) (ns(t) * self)
{
    ns(clear) (self);
    free(self->_header);
    return 0;
}

// Modification
int ns(insert) (ns(t) *self, size_t pos, const void * element)
{
    ns(node_t) * p = self->_header;

    for(int i = 0; i < pos; ++ i)
    {        
        p = p->next;

        if(p == self->_header)
        {
            _debug("Index Out Of Range[0,len].\nOperation Aborted","WARN");
            try_error(duList_INDEX_OUT_OF_BOUND);
            return -2;
        }


    }

    ns(node_t) *p_newnode = (ns(node_t) *)malloc(sizeof(ns(node_t)) + self->_elem_size);


    if(p_newnode == NULL)
    {
        _debug("Allocation Fault","WARN");
        try_error(duList_INSUFFICIENT_MEMORORY);

        return -1;
    }

    p->next->prev = p_newnode;
    p_newnode->next = p->next;
    p_newnode->prev = p;
    p->next = p_newnode;
    memcpy(p_newnode->data,element,self->_elem_size);

    return 0;
}

int ns(insert_reverse) (ns(t) * self, size_t pos, const void * element)
{
    
    
    ns(node_t) * p = self->_header;
    
    for(int i = 0; i < pos; ++ i)
    {
        p = p->prev;
        
        if(p == self->_header)
        {
            _debug("Index Out Of Range[0,len].\nOperation Aborted","WARN");
            try_error(duList_INDEX_OUT_OF_BOUND);

            return -2;
        }
    }

    ns(node_t) *p_newnode = (ns(node_t) *)malloc(sizeof(ns(node_t)) + self->_elem_size);


    if(p_newnode == NULL)
    {
        _debug("Allocation Fault","WARN");
        try_error(duList_INSUFFICIENT_MEMORORY);

        return -1;
    }

    p->prev->next = p_newnode;
    p_newnode->prev = p->prev;
    p->prev = p_newnode;
    p_newnode->next = p;

    memcpy(p_newnode->data,element,self->_elem_size);

    return 0;

}


int ns(erase)  (ns(t) * self, size_t pos)
{
     
    ns(node_t) * p = self->_header->next;
    
    if(p == self->_header)
    {
        _debug("Error: Delete from empty list.\nOperation Aborted","WARN");    
        try_error(duList_INDEX_OUT_OF_BOUND);

        return -3;
    }
    
    for(int i = 0; i < pos; ++ i)
    {
        
        if(p->next == self->_header)
        {
            _debug("Index Out Of Range[0,len-1].\nOperation Aborted","WARN");
            try_error(duList_INDEX_OUT_OF_BOUND);

            return -2;
        }

        p = p->next;
        
    }

    p->prev->next = p->next;
    p->next->prev = p->prev;
    free(p);

    return 0;
}


int ns(erase_reverse)  (ns(t) * self, size_t pos)
{

    ns(node_t) * p = self->_header->prev;
    
    if(p == self->_header)
    {
        _debug("Error: Delete from empty list.\nOperation Aborted","WARN");    
        try_error(duList_INDEX_OUT_OF_BOUND);

        return -3;
    }
    
    for(int i = 0; i < pos; ++ i)
    {
        
        if(p->prev == self->_header)
        {
            _debug("Index Out Of Range[0,len-1].\nOperation Aborted","WARN");
            return -2;
        }

        p = p->prev;
        
    }

    p->prev->next = p->next;
    p->next->prev = p->prev;
    free(p);

    return 0;
}


int ns(clear)  (ns(t) * self)
{
    ns(node_t) *p = self->_header->next;

    while (p != self->_header)
    {
        p->prev->next = p->next;
        p->next->prev = self->_header;
        free(p);
        p = self->_header->next;
    }

    return 0;
}

// Utility
int ns(is_empty) (const ns(t) * self)
{
    return self->_header->next == self->_header;
}

size_t ns(length)   (const ns(t) * self)
{
    ns(node_t) *p = self->_header->next;
    size_t cnt = 0;
    while (p != self->_header)
    {
        cnt ++;
        p = p->next;
    }
    
    return cnt;
}

int ns(get) (const ns(t) * self, size_t pos, void * val_dest)
{
    ns(node_t) * p = self->_header->next;

    if(p == self->_header)
    {
        _debug("Error: Delete from empty list.\nOperation Aborted","WARN");    
        return -3;
    }
    
    for(int i = 0; i < pos; ++ i)
    {
        if(p == self->_header)
        {
            _debug("Index Out Of Range[0,len-1].\nOperation Aborted","WARN");
            return -2;
        }
        p = p->next;
    }
    
    memcpy(val_dest, p->data, self->_elem_size);

    return 0;
}


int ns(get_reverse) (const ns(t) * self, size_t pos, void * val_dest)
{
    
    ns(node_t) * p = self->_header->prev;

    if(p == self->_header)
    {
        _debug("Error: Delete from empty list.\nOperation Aborted","WARN");    
        return -3;
    }
    
    for(int i = 0; i < pos; ++ i)
    {
        if(p == self->_header)
        {
            _debug("Index Out Of Range[0,len-1].\nOperation Aborted","WARN");
            return -2;
        }
        p = p->prev;
    }
    
    memcpy(val_dest, p->data, self->_elem_size);

    return 0;

}

#undef try_error
#undef ns